package com.strong.config;

import com.strong.config.security.filter.JwtRequestFilter;
import com.strong.config.security.handler.JwtAccessDeniedHandler;
import com.strong.config.security.handler.JwtAuthenticationFailureHandler;
import com.strong.config.security.handler.JwtAuthenticationSuccessHandler;
import com.strong.config.security.handler.JwtLogoutSuccessHandler;
import com.strong.config.security.point.JwtAuthenticationEntryPoint;
import com.strong.config.security.userdetails.JwtUserDetailsService;
import com.strong.utils.security.Sm2PasswordEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

/**
 * Web安全配置
 *
 * @author Administrator
 * @date 2023/09/11
 */
@Slf4j
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class WebSecurityConfig {

    /**
     * 注销URL地址
     */
    public static final String STR_URL_LOGOUT_URL = "/logout";

    /**
     * 登录URL地址
     */
    public static final String STR_URL_LOGIN_URL = "/login";

    /**
     * 主页HTML地址
     */
    public static final String STR_URL_INDEX_HTML_URL = "/index.html";

    /**
     * 默认主页HTML地址
     */
    public static final String STR_URL_INDEX_URL = "/";

    /**
     * 用户验证服务 {@link JwtUserDetailsService}
     */
    private final UserDetailsService userDetailsService;

    /**
     * 身份验证成功处理程序 {@link JwtAuthenticationSuccessHandler}
     */
    private final AuthenticationSuccessHandler authenticationSuccessHandler;

    /**
     * 身份验证失败的处理程序 {@link JwtAuthenticationFailureHandler}
     */
    private final AuthenticationFailureHandler authenticationFailureHandler;

    /**
     * 登出成功处理程序 {@link JwtLogoutSuccessHandler}
     */
    private final LogoutSuccessHandler logoutSuccessHandler;

    /**
     * JWT认证入口点 {@link JwtAuthenticationEntryPoint}
     */
    private final AuthenticationEntryPoint authenticationEntryPoint;

    /**
     * JWT认证入口点 {@link JwtAccessDeniedHandler}
     */
    private final AccessDeniedHandler accessDeniedHandler;

    /**
     * JWT请求过滤
     */
    private final JwtRequestFilter jwtRequestFilter;

    /**
     * 实例化
     *
     * @param userDetailsService           用户验证服务
     * @param authenticationSuccessHandler 身份验证成功处理程序
     * @param authenticationFailureHandler 身份验证失败的处理程序
     * @param logoutSuccessHandler         登出成功处理程序
     * @param authenticationEntryPoint     认证入口点
     * @param jwtRequestFilter             JWT请求过滤
     */
    public WebSecurityConfig(@Qualifier("JwtUserDetailsService") UserDetailsService userDetailsService,
                             @Qualifier("JwtAuthenticationSuccessHandler") AuthenticationSuccessHandler authenticationSuccessHandler,
                             @Qualifier("JwtAuthenticationFailureHandler") AuthenticationFailureHandler authenticationFailureHandler,
                             @Qualifier("JwtLogoutSuccessHandler") LogoutSuccessHandler logoutSuccessHandler,
                             @Qualifier("JwtAuthenticationEntryPoint") AuthenticationEntryPoint authenticationEntryPoint,
                             @Qualifier("JwtAccessDeniedHandler") AccessDeniedHandler accessDeniedHandler,
                             JwtRequestFilter jwtRequestFilter) {
        this.userDetailsService = userDetailsService;
        this.authenticationSuccessHandler = authenticationSuccessHandler;
        this.authenticationFailureHandler = authenticationFailureHandler;
        this.logoutSuccessHandler = logoutSuccessHandler;
        this.authenticationEntryPoint = authenticationEntryPoint;
        this.accessDeniedHandler = accessDeniedHandler;
        this.jwtRequestFilter = jwtRequestFilter;
    }

    /**
     * 安全过滤链
     *
     * @param httpSecurity http安全性
     * @return {@link SecurityFilterChain}
     * @throws Exception 异常
     */
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        // CSRF禁用
        httpSecurity.csrf(AbstractHttpConfigurer::disable);

        // 禁用basic明文验证
        httpSecurity.httpBasic(AbstractHttpConfigurer::disable);

        // 强制session无效，使用jwt认证时建议禁用，正常登录不能禁用session
        httpSecurity.sessionManagement(httpSecuritySessionManagementConfigurer ->
                httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        );

        // 加入异常处理器
        httpSecurity.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
                // 加入JWT认证入口点
                httpSecurityExceptionHandlingConfigurer
                        .authenticationEntryPoint(authenticationEntryPoint)
                        .accessDeniedHandler(accessDeniedHandler)
        );

        // 使用自定义登录
        httpSecurity.formLogin(httpSecurityFormLoginConfigurer -> {
            // 自定义登录页位置
            httpSecurityFormLoginConfigurer.loginPage(STR_URL_LOGIN_URL).permitAll();
            // 注册验证成功处理器
            httpSecurityFormLoginConfigurer.successHandler(authenticationSuccessHandler);
            // 注册验证失败处理器
            httpSecurityFormLoginConfigurer.failureHandler(authenticationFailureHandler);
        });

        // 使用自定义注销
        httpSecurity.logout(httpSecurityLogoutConfigurer -> {
            // 自定义登出位置
            httpSecurityLogoutConfigurer.logoutUrl(STR_URL_LOGOUT_URL).permitAll();
            // 自定义登出成功处理器
            httpSecurityLogoutConfigurer.logoutSuccessHandler(logoutSuccessHandler);
        });

        // 请求授权
        httpSecurity.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
                authorizationManagerRequestMatcherRegistry
                        // 允许访问index的html页面
//                        .requestMatchers(STR_URL_INDEX_HTML_URL, STR_URL_INDEX_URL).permitAll()
                        .requestMatchers("/**").permitAll()
                        // 允许任意请求被已登录用户访问，不检查Authority
                        .anyRequest().authenticated());

        // 在登出过滤器之前加入JWT请求过滤器
        httpSecurity.addFilterBefore(jwtRequestFilter, LogoutFilter.class);

        return httpSecurity.build();
    }

    /**
     * 身份验证提供者
     *
     * @return {@link DaoAuthenticationProvider}
     */
    @Bean
    DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(new Sm2PasswordEncoder());
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        return daoAuthenticationProvider;
    }
}
